require_relative "XMLModel"

module XMLHelper
  class XMLDocumentBuilder
    def initialize(file_name)
      @file = File.open(file_name, "r")
      @file_contents = @file.read
    end

    def close
      @file.close
    end

    def build_xml
      xml_document = XMLModel::Document.new()
      parser(xml_document)
      return xml_document
    end

    def parser(xml_document)
      expl = XMLExplorer.new(xml_document)
      tokens = lexer

      tokens.each do |token,value|
        case token
        when :openingtag
          expl.get_cursor.add_child(XMLModel::Element.new(value))
          expl.set_cursor(expl.get_cursor.get_last_child)
        when :closingtag
          expl.go_up
        when :selfclosingtag
          expl.get_cursor.add_child(XMLModel::Element.new(value))
        when :comment
          expl.get_cursor.add_child(XMLModel::Comment.new(value))
        when :text
          expl.get_cursor.add_child(XMLModel::PlainText.new(value))
        end
      end
    end

    def lexer
      characters = @file_contents
      token_exprs = { 
        selfclosingtag: /\A<([^\/<>]+)\/>/, 
        closingtag: /\A<\/([^\/<>]+)>/,
        openingtag: /\A<([^\/<>]+)>/,
        comment: /\A<!--([^\/<>]+)-->/,
        whitespace: /\A([\s\r\n]+)/,
        text: /\A([\w \t]+)/
      }

      pos = 0
      tokens = []

      while pos < characters.size
        mch = nil
        for token_expr in token_exprs
          tag, pattern = token_expr
          mch = characters[pos..-1].match(pattern)
          if mch
            text = mch[1]
            if tag
              unless tag == :whitespace
                token = [tag,text]
                tokens << token
              end
              break
            end
          end
        end
        if not mch
          puts "illegal character: #{characters[pos]}"
          return
        else
          pos += mch.end(0)
        end
      end
      return tokens
    end
  end

  class XMLExplorer
    def initialize(document)
      @xml_document = document
      @cursor = @xml_document.get_root
    end

    def next
      if @cursor.get_first_child
        @cursor = @cursor.get_first_child
      elsif @cursor.next_sibling
        @cursor = @cursor.next_sibling
      elsif @cursor.get_parent && @cursor.get_parent.next_sibling
        @cursor = @cursor.get_parent.next_sibling
      end
      self
    end

    def previous
      if @cursor.previous_sibling
        @cursor = @cursor.previous_sibling
        while(@cursor.get_last_child)
          @cursor = @cursor.get_last_child
        end
      elsif @cursor.get_parent
        @cursor = @cursor.get_parent
      end
      self
    end

    def go_up
      if @cursor.get_parent
        @cursor = @cursor.get_parent
      end
      self
    end

    def go_inside
      if @cursor.get_first_child
        @cursor = @cursor.get_first_child
      end
      self
    end

    def get_cursor
      @cursor
    end

    def set_cursor(node)
      @cursor = node
    end
  end
end

builder = XMLHelper::XMLDocumentBuilder.new("test.xml")
xml = builder.build_xml
builder.close

explr = XMLHelper::XMLExplorer.new(xml)

# xml.show

# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.next
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.previous
# explr.get_cursor.show
# explr.get_cursor.show

# explr.next.next.next.next.next.next.next
# explr.get_cursor.show
# explr.go_up.go_up
# explr.get_cursor.show

xml.show_current(explr.get_cursor)
4.times {
  explr.next
  puts ""
  xml.show_current(explr.get_cursor)
}

# puts xml

# puts xml.inspect